package gu.sql2java.excel;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import com.google.common.collect.Lists;

import gu.sql2java.excel.config.SheetConfig;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * 基于apache POI实现数据记录导出为Excel,支持多Sheet导出
 * @author guyadong
 *
 */
public class ExcelGenerator extends BaseExcelWriter {

	private final Workbook workbook = new XSSFWorkbook();
    private final List<SheetGenerator<?>> sheetGenerators = Lists.newLinkedList();
	/**
	 * 默认构造方法
	 */
	public ExcelGenerator() {
	}
	/**
	 * 构造方法
	 * @param rows 数据记录对象列表
	 * @param beanClass 数据记录对象的类型
	 */
	public <B> ExcelGenerator(Iterable<B> rows, Class<B> beanClass) {
		addSheetGenerator(new SheetGenerator<B>(rows, checkNotNull(beanClass,"beanClass is null"),Collections.emptyList()));
	}
	/**
	 * @param rows 数据记录对象列表
	 * @param beanClass 数据记录类型(Map,Java Bean)
	 * @param includeCoumns 输出字段白名单,只输出在名单内的字段，为{@code null}或空输出所有字段,
	 * 										  Map类型记录无法预知类型的字段完整信息,所以对于Map类型的记录,如果不指定此字段，
	 *                                        则要对{@code Iterable}中所有的Map记录遍历，才能获取完整的字段信息，效率较低
	 *                                        所以建议对于Map类型记录要定义此参数
	 */
	public <B> ExcelGenerator(Iterable<B> rows,Class<B> beanClass, Iterable<String> includeColumns) {
		addSheetGenerator(new SheetGenerator<>(rows, beanClass, includeColumns));
	}
	/**
	 * 构造方法
	 * @see #ExcelGenerator(Iterable, Class,Iterable)
	 */
	public <B> ExcelGenerator(Iterable<B> iterable, Class<B> beanClass,String... includeCoumns){
		this(iterable,beanClass,null == includeCoumns ? null : Arrays.asList(includeCoumns));
	}
	/**
	 * 构造方法
	 * @param beanClass 数据记录类型(Map,Java Bean)
	 * @param includeCoumns 输出字段白名单,只输出在名单内的字段，为{@code null}或空输出所有字段,
	 * 										  Map类型记录无法预知类型的字段完整信息,所以对于Map类型的记录,如果不指定此字段，
	 *                                        则要对{@code Iterable}中所有的Map记录遍历，才能获取完整的字段信息，效率较低
	 *                                        所以建议对于Map类型记录要定义此参数
	 */
	public <B> ExcelGenerator(Class<B> beanClass, Iterable<String> includeColumns) {
		this((Object)null, beanClass, includeColumns);
	}
	/**
	 * @param rows 数据记录对象列表
	 * @param beanClass 数据记录类型(Map,Java Bean)
	 * @param includeCoumns 输出字段白名单,只输出在名单内的字段，为{@code null}或空输出所有字段,
	 * 										  Map类型记录无法预知类型的字段完整信息,所以对于Map类型的记录,如果不指定此字段，
	 *                                        则要对{@code Iterable}中所有的Map记录遍历，才能获取完整的字段信息，效率较低
	 *                                        所以建议对于Map类型记录要定义此参数
	 */
	public <B> ExcelGenerator(Object rows,Class<B> beanClass, Iterable<String> includeColumns) {
		addSheetGenerator(new SheetGenerator<>(rows,beanClass,includeColumns));
	}
	/**
	 * 在指定索引位置增加一个工作薄生成器
	 * @param index 为{@code null}则在尾部增加 
	 * @param sheetGenerator 为{@code null}忽略
	 * @return 当前对象
	 * @since 3.13.0
	 */
	public ExcelGenerator addSheetGenerator(Integer index,SheetGenerator<?> sheetGenerator){
		if(null != sheetGenerator){
			sheetGenerator.setWorkbook(workbook);
			if(null == index){
				sheetGenerators.add(sheetGenerator);
			}else{				
				sheetGenerators.add(index,sheetGenerator);
			}
		}
		return this;
	}
	/**
	 * 在尾部增加一个工作薄生成器
	 * @param sheetGenerator
	 * @return 当前对象
	 * @since 3.13.0
	 */
	public ExcelGenerator addSheetGenerator(SheetGenerator<?> sheetGenerator){
		return addSheetGenerator(null, sheetGenerator);
	}
	/**
	 * 返回索引指定{@link SheetGenerator} 的输出配置({@link SheetConfig})实例,可以通过此实例修改输出配置,
	 * 索引越界则返回{@code null}
	 * @since 3.13.0
	 */
	public SheetConfig getSheetConfig(int index) {
		try{
		return sheetGenerators.get(index).getSheetConfig();
		} catch (IndexOutOfBoundsException e) {
			return null;
		}
	}
	/**
	 * 设置索引指定{@link SheetGenerator} 的输出配置({@link SheetConfig})实例,索引越界则忽略
	 * @param index
	 * @param sheetConfig
	 * @return 当前对象
	 * @since 3.13.0
	 */
	public ExcelGenerator setSheetConfig(int index,SheetConfig sheetConfig) {
		try {
			sheetGenerators.get(index).setSheetConfig(sheetConfig);
		} catch (IndexOutOfBoundsException e) {
		}
		return this;
	}
	public ExcelGenerator setRowLimitForAutoColumnSize(int rowLimitForAutoColumnSize) {
	    sheetGenerators.forEach(s->s.setRowLimitForAutoColumnSize(rowLimitForAutoColumnSize));
	    return this;
	}
	/**
	 * 返回第一个{@link SheetGenerator} 的输出配置({@link SheetConfig})实例,可以通过此实例修改输出配置,
	 * 实例不存在则返回{@code null}
	 * @see #getSheetConfig(int)
	 */
	public SheetConfig getSheetConfig() {
		return getSheetConfig(0);
	}
	/**
	 * 设置第一个{@link SheetGenerator} 的输出配置({@link SheetConfig})实例,
	 * 实例不存在则忽略
	 * @param sheetConfig
	 * @return 当前对象
	 * @see #setSheetConfig(int, SheetConfig)
	 */
	public ExcelGenerator setSheetConfig(SheetConfig sheetConfig) {
		return setSheetConfig(0,sheetConfig);
	}

	@Override
	protected void write() throws IOException {
		for(SheetGenerator<?> g:sheetGenerators){
		    g.write();
		}
	}

	@Override
    protected void write(OutputStream outputStream) throws IOException {
	    if(!sheetGenerators.isEmpty()){
	        sheetGenerators.get(0).write(outputStream);
	    }
    }
    @Override
	public Workbook getWorkbook() {
		return workbook;
	}
	/**
	 * 返回当前所有的{@link SheetGenerator}实例列表
	 */
	public List<SheetGenerator<?>> getSheetGenerators() {
		return sheetGenerators;
	}
}
